Dynamic loading
Dynamic loading (dynamické načítání) je v informatice označení pro mechanismus, který načte knihovnu do paměťového prostoru za běhu procesu. Běžící proces tak získá možnost volat procedury a funkce, které se nacházejí v načtené knihovně. Knihovnu lze později z paměti uvolnit. Mechanismus umožňuje programátorovi v případě nedostupnosti knihovny provést náhradní činnost (použít alternativní knihovnu, zobrazit hlášení uživateli a podobně). Tím se odlišuje od klasického zavádění dynamických knihoven při spuštění programu, kdy musí být přítomny všechny potřebné knihovny.
Historie
[editovat | editovat zdroj]Dynamické načítání bylo běžným mechanismem pro operační systém IBM/360 (začátek 60. let až do současné Z/Architektury) a to zejména pro obsluhu vstupně-výstupních rutin, COBOL a PL/1 běhových knihoven. Co se týče programování aplikací, nahrávání je do značné míry transparentní, protože je většinou vyřešené na úrovni operačního systému. IBM využívá dynamické načtení od 70. let 20. století u transakčního zpracování strategického systému CICS a to jak pro jádro operačního systému, tak i pro normální aplikační programy. Opravy pak mohou být uskutečněny za běhu systému nebo programu bez nutnosti jejich restartu.
Mezi hlavní výhody patří:
- opravy subsystému opravují celý program bez nutnosti jeho nového linkování
- knihovny mohou být chráněny před neautorizovanou úpravou
Použití
[editovat | editovat zdroj]Dynamické načítání je nejčastěji používáno v implementaci softwarových pluginů (modulární struktura Apache HTTP Serveru). Nebo v programech, které nabízejí více podporovaných knihoven a uživatel si je jednotlivě může volit (rozšiřující knihovny pro PHP).
C/C++
[editovat | editovat zdroj]Dynamické načítání nepodporují všechny systémy. Mac OS X, Linux a Solaris nabízí dynamické načítání prostřednictvím knihovních „dl“ funkcí v jazyce C. Operační systém Windows podporuje dynamické načítání pomocí volání Windows API.
Shrnutí
[editovat | editovat zdroj]Název | Standard POSIX/UNIX API | Microsoft Windows API |
---|---|---|
Zařazení hlavičkového souboru | #include <dlfcn.h>
|
#include <windows.h>
|
Definice hlavičky | dl
( |
Kernel32.dll
|
Načtení knihovny | dlopen
|
LoadLibrary LoadLibraryEx |
Extrahování obsahu | dlsym
|
GetProcAddress
|
Zavření knihovny | dlclose
|
FreeLibrary
|
Načtení knihovny
[editovat | editovat zdroj]Načtení knihovny se provede prostřednictvím LoadLibrary
nebo LoadLibraryEx
na operačním systému Windows a pomocí dlopen
na operačních systémech na bázi Linuxu. Následují příklady:
Linux, *BSD, Solaris, etc.
[editovat | editovat zdroj]void* sdl_library = dlopen("libSDL.so", RTLD_LAZY);
if(sdl_library == NULL) {
// oznámení chyby ...
} else {
// výsledek zavoláním dlsym
}
Mac OS X
[editovat | editovat zdroj]UNIX library:
void* sdl_library = dlopen("libsdl.dylib", RTLD_LAZY);
if(sdl_library == NULL) {
// oznámení chyby ...
} else {
// výsledek zavoláním dlsym
}
void* sdl_library = dlopen("/Library/Frameworks/SDL.framework/SDL", RTLD_LAZY);
if(sdl_library == NULL) {
// oznámení chyby ...
} else {
// výsledek zavoláním dlsym
}
Windows
[editovat | editovat zdroj]HMODULE sdl_library = LoadLibrary("SDL.dll");
if( sdl_library == NULL) {
//oznámení chyby ...
} else {
// výsledek zavoláním GetProcAddress
}
Extrahování obsahu knihovny
[editovat | editovat zdroj]Extrahování obsahu dynamicky načítané knihovny je dosaženo pomocí příkazu GetProcAddress ve Windows a dlsym v Unix systému.
Linux, *BSD, Mac OS X, Solaris, etc.
[editovat | editovat zdroj]void* initializer = dlsym(sdl_library,"SDL_Init");
if(initializer == NULL) {
// oznámení chyby ...
} else {
...
}
Windows
[editovat | editovat zdroj]FARPROC initializer = GetProcAddress(sdl_library,"SDL_Init");
if(initializer == NULL) {
// report error ...
} else {
...
}
Převod extrahovaného obsahu knihovny
[editovat | editovat zdroj]Vrácený výsledek prostřednictvím dlsym()
nebo GetProcAddress()
musí být převeden do požadované destinace předtím, než může být použit.
Windows
[editovat | editovat zdroj]V případě systému Windows, konverze je jednoduchá, protože FARPROC je v podstatě již ukazatel funkce:
typedef INT_PTR (*FARPROC)(void);
Může nastat problém, pokud adresy objektu jsou vyvolány místo funkce. Nicméně, obvykle jeden chce získat funkce stejně, takže to není ve výsledku problém.
typedef void (*sdl_init_function_type)(void);
sdl_init_function_type init_func = (sdl_init_function_type) initializer;
UNIX (POSIX)
[editovat | editovat zdroj]Dle specifikace POSIX je výsledkem provedení dlsym()
prázdný ukazatel (pointer), což způsobuje problém. Je to způsobeno tím, že programovací jazyky C a C++ nedovolují provést konverzi mezi ukazateli na objekty a ukazateli funkcí (není vyžadováno aby měl ukazatel funkce stejnou velikost jako ukazatel na objekt). Je tedy striktně zakázáno provést konverzi mezi typem void*
a ukazatelem na funkci. Na většině v současnosti používaných systémů jsou ukazatele na funkce s ukazateli na objekty mezi sebou již konvertibilní.
Následující fragment kódu ukazuje jeden ze způsobů řešení, který umožňuje provádět tuto konverzi v různých systémech:
typedef void (*sdl_init_function_type)(void);
sdl_init_function_type init_func = (sdl_init_function_type)initializer;
Výše uvedená část programového kódu může u některých kompilátorů vyvolat toto varovné hlášení: „Přejmenování ukazatele dereferenčního typu porušuje pravidla aliasingu“. Řešením je použití následujícího programového kódu:
typedef void (*sdl_init_function_type)(void);
union { sdl_init_function_type func; void * obj; } alias;
alias.obj = initializer;
sdl_init_function_type init_func = alias.func;
Tím se zakáže varovné hlášení.
Související články
[editovat | editovat zdroj]Externí odkazy
[editovat | editovat zdroj]- General Links
- C/C++ UNIX API:
- C/C++ Windows API:
- Java API: